Skip to content
字数
1072 字
阅读时间
5 分钟

1. 核心实现方式

  • Vue 2:基于 Object.defineProperty
    通过遍历数据对象的属性,使用 Object.defineProperty 为每个属性添加 gettersetter,从而拦截属性的读取和修改操作。
    原理:当属性被访问时(getter),收集依赖(记录当前组件的 Watcher);当属性被修改时(setter),触发依赖更新(通知 Watcher 重新渲染)。

  • Vue 3:基于 Proxy
    使用 ES6 的 Proxy 代理整个数据对象,直接拦截对象的 读取、修改、新增、删除 等操作(包括属性的动态添加/删除)。
    原理:通过 Proxy 创建数据的代理对象,当对代理对象进行操作时,触发对应的拦截方法(如 getsetdeleteProperty 等),进而收集依赖或触发更新。

2. 对数据类型的支持

  • Vue 2

    • 仅能拦截对象的 已有属性,无法直接监测 新增属性删除属性(需通过 Vue.set/this.$setVue.delete/this.$delete 手动触发响应式)。
    • 对数组的拦截有限:通过重写数组的 7 个变更方法(pushpopshiftunshiftsplicesortreverse)实现响应式,而直接通过索引修改数组元素(如 arr[0] = 1)或修改数组长度(如 arr.length = 0)无法触发更新(需手动处理)。
  • Vue 3

    • Proxy 能原生拦截对象的 所有操作,包括新增属性(obj.newKey = value)、删除属性(delete obj.key),无需手动调用 API。
    • 对数组的支持更完善:直接通过索引修改元素(arr[0] = 1)或修改长度(arr.length = 0)都能被 Proxy 拦截,自动触发响应式更新,无需重写数组方法。

3. 初始化性能

  • Vue 2
    在初始化时需要 递归遍历对象的所有属性,为每个属性添加 getter/setter,对于嵌套较深或属性较多的对象,初始化性能开销较大。

  • Vue 3
    Proxy懒代理 模式,初始化时仅代理顶层对象,嵌套对象的代理会在首次访问时动态创建(即“惰性递归”)。因此,初始化大型对象时性能更优,尤其适合数据结构复杂的场景。

4. 依赖收集粒度

  • Vue 2
    依赖收集以 属性为单位,每个属性对应一个依赖列表。当属性更新时,仅通知依赖该属性的 Watcher。

  • Vue 3
    依赖收集以 对象为单位,但通过 Proxy 的拦截能力,可以更精确地定位变化(如具体修改的属性)。同时,Vue 3 引入了 WeakMap + Map 的依赖存储结构,进一步优化了依赖管理的效率

5. 对非对象类型的处理

  • Vue 2
    对基本类型(如字符串、数字)的响应式依赖于对象属性的包装(即需将基本类型放在对象中,如 { count: 0 }),否则无法拦截。

  • Vue 3
    虽然 Proxy 本质上代理对象,但通过 ref 语法糖,将基本类型包装为一个带有 value 属性的对象,间接实现了基本类型的响应式(ref 内部仍基于 ProxyObject.defineProperty 实现,取决于版本)。

总结

特性Vue 2Vue 3
核心 APIObject.definePropertyProxy
新增/删除属性需手动调用 Vue.set/delete原生支持,自动响应
数组索引/长度修改不支持(需手动处理)原生支持
初始化性能递归遍历所有属性,开销较大懒代理,初始化性能更优
依赖收集粒度以属性为单位以对象为单位,定位更精确

Vue 3 的响应式原理在灵活性、性能和对数据操作的全面性上都优于 Vue 2,这也是 Vue 3 性能提升的重要原因之一。

贡献者

The avatar of contributor named as jiechen jiechen
The avatar of contributor named as chenjie chenjie

页面历史

撰写